package me.DonglinPu.COMP2039;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;

/*
 * LOG:
 * HELIX correct prediction = 27.0%
 * HELIX cc=2.973462614910671
 * SHEET correct prediction = 78.0%
 * SHEET cc=0.9441718845283921
 */
public class Test {
	private String input_file_name = "testinput.dl.txt";
	private String output_file_name = "testBinary101.dl";
	private String test_final_weight = "testFinalWeight.dl";
	private String test_test = "testTest.dl";
	
	private static final int NUMBER_OF_LOOP = 30;

	private static final double LEARNING_RATE = 0.02;
	
	FileInputStream fis;
	DataInputStream dis;
	BufferedReader br;
	FileWriter fw;
	BufferedWriter bw;
	
	private double[] weights;
	int[] Xn;
	double sum;
	String vals;
	int desired_result;
	String line;
	int p = 0;
	int n = 0;
	int o = 0;
	int u = 0;
	
	int correct = 0;
	int incorrect = 0;
	public Test() throws IOException{
		run();
		learn();

		write_final_weights_to_file();
		
		predict_helix(weights);
		
		System.out.println(calculate_cc(p, n, o, u));
		System.out.println(correct/(correct+incorrect));
	}
	
	private void run() throws IOException{
		Data.writeBinary("h", input_file_name, output_file_name);
	}
	

	private double calculate_cc(int p, int n, int o, int u) {
		// TODO Auto-generated method stub
		int pn = p*n;
		int ou = o*u;
		int pPo = p+o;
		int pPu = p+u;
		int nPo = n+o;
		int nPu = n+u;
//		System.out.println("pn-ou="+(pn-ou));
//		System.out.println("p+o="+pPo);
//		System.out.println("p+u="+pPu);
//		System.out.println("n+o="+nPo);
//		System.out.println("n+u="+nPu);
//		System.out.println(pPo*pPu*nPo*nPu);
//		System.out.println(Math.sqrt(pPo*pPu*nPo*nPu));
		return (pn-ou)/Math.sqrt(pPo*pPu*nPo*nPu);
	}

	private void write_final_weights_to_file() throws IOException {
		fw = new FileWriter(test_final_weight);
		bw = new BufferedWriter(fw);
		for(int i = 0; i < weights.length; i++){
			bw.write(weights[i]+"\r\n");
		}
		bw.close();
	}

	public void learn() throws IOException{
		weights = new double[101];
		Xn = new int[101];
		weights = init_weight(weights);//assign initial weight 0.2
		
		for(int i = 0; i < NUMBER_OF_LOOP; i++){
			loop();
		}
	}
	
	private void loop() throws IOException{
		fis = new FileInputStream(output_file_name);
		dis = new DataInputStream(fis);
		br = new BufferedReader(new InputStreamReader(dis));
		
		sum = 0;
		vals = "";//[000....001] - 101 string
		desired_result = 0;//Desired result: 1 or 0
		line = br.readLine();
		
		while(line != null){
//			vals = line.substring(1, 102);//get the values of this line
//			desired_result = new Integer(""+line.charAt(line.length()-1));//get desired result of this line
			while(true){// loop of recalculate weights
				if(line.equals("==")){
//					write_end_of_protein(bw);
					break;
				}
				vals = line.substring(1, 102);//get the values of this line
				desired_result = new Integer(""+line.charAt(line.length()-1));//get desired result of this line
				Xn = get_Xn(vals);//assign all values of Xn
				sum = calculate_sum(weights, Xn);//calculate sum
				//Algorithm: Wi(t+1) = Wi(t) + LEARNING_RATE*(disired_result - old_sum)*Xi
				weights = calculate_new_weights(weights, desired_result, sum, Xn);
//				write_weights(weights, bw);
				line = br.readLine();
			}
			line = br.readLine();
		}
//		bw.close();
		//print_final_weights(weights);
	}

	private void predict_helix(double[] weights) throws IOException {
		FileInputStream fis = new FileInputStream(output_file_name);
		DataInputStream dis = new DataInputStream(fis);
		BufferedReader br = new BufferedReader(new InputStreamReader(dis));
		
		FileWriter fw = new FileWriter(test_test);
		BufferedWriter bw = new BufferedWriter(fw);

		int[] Xn = new int[101];
		double sum = 0;
		String vals = "";//[000....001] - 101 string
		
		String line = br.readLine();
//		weights = init_weight(weights);//assign initial weight 0.2
		while(line != null){
//			vals = line.substring(1, 102);//get the values of this line
//			desired_result = new Integer(""+line.charAt(line.length()-1));//get desired result of this line
			while(true){
				if(line.equals("==")){
//					write_end_of_protein(bw);
					break;
				}
				vals = line.substring(1, 102);//get the values of this line
				desired_result = new Integer(""+line.charAt(line.length()-1));//get desired result of this line
				Xn = get_Xn(vals);//assign all values of Xn
				sum = calculate_sum(weights, Xn);//calculate sum
				if(sum > 0){
					if(desired_result == 1){
						correct++;
						p++;
					} else {
						incorrect++;
						o++;
					}
					bw.write("D="+desired_result+" my=1"+"\r\n");
				} else {
					if(desired_result == 0){
						correct++;
						n++;
					} else {
						incorrect++;
						u++;
					}
					bw.write("D="+desired_result+" my=0"+"\r\n");
				}
				//Algorithm: Wi(t+1) = Wi(t) + LEARNING_RATE*(disired_result - old_sum)*Xi
//				weights = calculate_new_weights(weights, desired_result, sum, Xn);
				line = br.readLine();
			}
			line = br.readLine();
		}
		bw.close();
	}

//	private void print_final_weights(double[] weights) {
//		for(int i = 0; i < weights.length; i++){
//			System.out.println("index: " + (i+1) + " weights:" + weights[i]);
//		}
//	}

	private void write_end_of_protein(BufferedWriter bw) throws IOException {
		bw.write("END OF PROTEIN\r\n");
	}

	private void write_weights(double[] weights, BufferedWriter bw) throws IOException {
		// TODO Auto-generated method stub
		for(int i = 0; i < weights.length; i++){
			bw.write(weights[i]+" ");
		}
		bw.write("\r\n");
	}

	private int[] get_Xn(String vals) {
		int[] Xn = new int[vals.length()];
		for(int i = 0; i < Xn.length; i++){
			Xn[i] = new Integer(""+vals.charAt(i));
		}
		return Xn;
	}

	private double[] init_weight(double[] weights) {
		for(int i = 0; i < weights.length; i++){
			weights[i] = 0.2;
		}
		return weights;
	}

	private double calculate_sum(double[] weights, int[] Xn) {
		double sum = 0;
		for(int i = 0; i < weights.length; i++){
			sum = sum + Xn[i]*weights[i];
		}
		return sum;
	}
	
	private double[] calculate_new_weights(double[] weights, int desired_result, double old_sum, int[] Xn) {
		int network;
		if(old_sum > 0){
			network = 1;
		} else {
			network = 0;
		}
//		System.out.println("old sum:"+old_sum);
		for(int i = 0; i < weights.length; i++){
			weights[i] = weights[i] + LEARNING_RATE*(desired_result - network)*Xn[i];
		}
//		System.out.println("weight9"+weights[9]);
		return weights;
	}
}
